#include "net/Pollers/EPoller.h"
#include "Base/Logger.h"
#include "Base/CurrectThread.h"
#include "net/Channel.h"
#include <unistd.h>
#include <errno.h>
#include <sys/epoll.h>
#include <cassert>

namespace toym
{
namespace net
{

EPoller::EPoller()
    : m_epollfd(::epoll_create1(EPOLL_CLOEXEC)),
      m_fd2Channel(),
      m_events(16)
{
    if (m_epollfd < 0) {
        CurrectThread::saveErrno(errno);
        LOG_ERROR << "epoll_create1 fail";
        ::abort();
    }
    LOG_DEBUG << "EPoller at [" << this << "]";
}

EPoller::~EPoller() {
    ::close(m_epollfd);
    m_epollfd = -1;
}

int EPoller::poll() {
    int ret = ::epoll_wait(m_epollfd, m_events.data(), static_cast<int>(m_events.size()), IPoller::intervalMicroseconds);
    if (ret > 0 && static_cast<std::size_t>(ret) == m_events.size()) {
        m_events.resize(m_events.size()*2);
    }
    return ret;
}

void EPoller::updateChannel(Channel& channel) {
    int fd = channel.getFd();
    int events = channel.getEvents();
    LOG_INFO << "updateChannle for fd = " << fd 
              << ", events = " << events;
    int idx = channel.getIdx();
    if (idx == s_New || idx == s_Deleted) {
        // 新添加的文件描述符
        if (idx == s_New) {
            assert(m_fd2Channel.find(fd) == m_fd2Channel.end());
            m_fd2Channel[fd] = &channel;
        } else {
            assert(m_fd2Channel.find(fd) != m_fd2Channel.end());
            assert(m_fd2Channel[fd] == &channel);
        }
        channel.setIdx(s_Added);
        update(EPOLL_CTL_ADD, channel);
    } else { // s_Added, 更新
        assert(m_fd2Channel.find(fd) != m_fd2Channel.end());
        assert(m_fd2Channel[fd] == &channel);
        assert(idx == s_Added);
        if (channel.isNonEvent()) {
            update(EPOLL_CTL_DEL, channel);
            channel.setIdx(s_Deleted);
        } else {
            update(EPOLL_CTL_MOD, channel);
        }
    }
}

void EPoller::removeChannel(Channel& channel) {
    int fd = channel.getFd();
    LOG_INFO << "removeChannel fd = " << fd;
    assert(m_fd2Channel.find(fd) != m_fd2Channel.end());
    assert(m_fd2Channel[fd] == &channel);
    assert(channel.isNonEvent());
    int idx = channel.getIdx();
    assert(idx == s_Deleted || idx == s_Added);
    m_fd2Channel.erase(fd);
    if (idx == s_Added) {
        update(EPOLL_CTL_DEL, channel);
    }
    channel.setIdx(s_New);
}



std::vector<Channel*> EPoller::getActiveChannels(int nActive) {
    assert(nActive > 0 && static_cast<std::size_t>(nActive) < m_events.size());
    std::vector<Channel*> ret;
    ret.reserve(nActive);
    for (std::size_t idx = 0; idx < static_cast<std::size_t>(nActive); ++idx) {
        Channel* pc = static_cast<Channel*>(m_events[idx].data.ptr);
        int fd = pc->getFd();
        assert(m_fd2Channel.find(fd) != m_fd2Channel.end());
        assert(m_fd2Channel[fd] == pc);
        pc->setRevnets(m_events[idx].events);
        ret.push_back(pc);
    }
    return ret;
}

void EPoller::update(int mode, Channel& channel) {
    struct epoll_event event;
    ::bzero(&event, sizeof(event));
    int fd = channel.getFd();
    event.data.fd = fd;
    event.events = channel.getEvents();
    event.data.ptr = &channel;
    LOG_DEBUG << "epoll_ctl op = " << mode << ", fd = " << channel.getFd()
              << ", event = " << channel.getEvents();
    if (::epoll_ctl(m_epollfd, mode, channel.getFd(), &event)) {
        CurrectThread::saveErrno(errno);
        if (mode == EPOLL_CTL_DEL) {
            LOG_WARNING << "epoll_ctl delete error";
            CurrectThread::saveErrno(0);
        } else {
            LOG_ERROR << " epoll_ctl error ";
            ::abort();
        }
    }
}


} // namespace net

} // namespace toym
